use feignhttp::Feign;
use hypers::prelude::*;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Extract, Default, PartialEq, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[extract(rename_all = "camelCase")] // 这个与上面的 #[serde(rename_all = "camelCase")] 是等效的
pub struct Data {
    pub email: Option<String>,
    pub age: Option<u16>,
    // #[extract(rename = "First-Name",alias("firstName"))]
    #[serde(rename = "firstName")]
    pub first_name: Option<String>,
}

#[derive(Serialize, Deserialize, Extract, Debug, PartialEq)]
#[extract(header)] //这个可以省略不写
pub struct HeaderParams {
    pub host: Option<String>,
    pub accept: Option<String>,
    #[extract(rename = "User-Agent", alias("User-Agent"))]
    pub user_agent: Option<String>,
}

#[derive(Serialize, Deserialize, Extract, Debug)]
#[extract(cookie)] //这个可以省略不写
pub struct CookieParams {
    hypers: Option<String>,
    rust: Option<String>,
}

#[derive(Serialize, Deserialize, Extract)]
#[query]
pub struct QueryParams {
    pub email: Option<String>,
    pub age: Option<u16>,
}

#[derive(Serialize, Deserialize, Extract, Feign, Debug)]
#[extract(body)] //这个可以省略不写
pub struct Input {
    #[extract(header)]
    pub host: Option<String>,

    #[extract(header)]
    //#[serde(rename(deserialize = "user-agent"))]
    #[serde(rename = "user-agent")]
    pub user_agent: Option<String>,

    #[extract(header)]
    pub accept: Option<String>,

    #[extract(cookie)]
    pub hypers: Option<String>,

    #[extract(cookie)]
    pub rust: Option<String>,

    #[extract(param)]
    pub id: Option<u16>,

    #[extract(query)]
    pub name: Option<String>,

    #[extract(query)]
    pub age: Option<u16>,

    #[serde(rename = "firstName")]
    pub first_name: Option<String>,

    #[extract(rename = "Last-Name", alias("lastName", "LastName"))]
    pub last_name: Option<String>,
}

#[derive(Serialize, Deserialize, Extract)]
#[extract(rename_all = "camelCase")]
struct RenameAllArg {
    first_name: Option<String>,
    last_name: Option<String>,
    user_name: Option<String>,
}
struct Api;
#[controller(name = "extract")]
impl Api {
    // http://127.0.0.1:7878/extract/header
    // Request Header Params
    #[get("/header")]
    pub async fn header(input: HeaderParams) -> impl Responder {
        (200, Json(input))
    }

    // http://127.0.0.1:7878/extract/admin@outlook.com/18
    // Uri Path Params
    #[delete("/:email/:age/:firstName")] // 参数是 email age
    pub async fn delete(data: Data) -> impl Responder {
        (200, Json(data))
    }

    // http://127.0.0.1:7878/extract/query?email=admin@outlook.com&age=19
    // Query Params
    #[get("/query")] // 参数是 email age
    pub async fn query(data: QueryParams) -> impl Responder {
        (200, Json(data))
    }

    // http://127.0.0.1:7878/extract/create
    // Context-Type : application/json
    #[post("/create")] // 参数是 email age
    pub async fn create(data: Data) -> Response {
        let mut res = Response::default();
        res.status(200).json(&data);
        res
    }

    // http://127.0.0.1:7878/extract/update
    // Context-Type : application/x-www-form-urlencoded
    #[put("/update")] // 参数是 email age
    async fn update(data: Data) -> Response {
        let mut res = Response::default();
        res.status(200).json(&data);
        res
    }

    // http://127.0.0.1:7878/extract/multipart_form
    // Context-Type : multipart/form-data Form Fields
    #[post("multipart_form")] // 参数是 email age
    pub async fn multipart_form(data: Data) -> Response {
        let mut res = Response::default();
        res.status(200).json(&data);
        res
    }

    // http://127.0.0.1:7878/extract/set_cookies
    #[get("/set_cookies")]
    pub async fn set_cookies() -> impl Responder {
        let mut cookie1 = Cookie::new("hypers", "hypers2023");
        cookie1.set_path("/extract");

        let mut cookie2 = Cookie::new("rust", "rust2023");
        cookie2.set_path("/extract");

        let mut cookie_jar = CookieJar::new();
        cookie_jar.add(cookie1);
        cookie_jar.add(cookie2);
        (200, cookie_jar)
    }

    // http://127.0.0.1:7878/extract/parse_cookies
    #[get("/parse_cookies")]
    pub async fn parse_cookies(cookie_param: CookieParams) -> impl Responder {
        (200, Json(cookie_param))
    }

    // 这个接口应该在接口测试工具上测试
    // http://127.0.0.1:7878/extract/extract_input/11?name=admin&age=18
    #[post("extract_input/:id")]
    pub async fn extract_input(params: Input) -> impl Responder {
        (200, Json(params))
    }

    #[post("rename_all")]
    pub async fn rename_all(arg: RenameAllArg) -> impl Responder {
        (200, Json(arg))
    }
}

#[derive(Serialize, Deserialize, Extract, Debug)]
#[query]
#[param]
#[body]
struct BadMan<'a> {
    #[serde(default)]
    id: i64,
    username: &'a str,
    first_name: String,
    last_name: &'a str,
    lovers: Vec<String>,
}
#[derive(Serialize, Deserialize, Extract, Debug)]
#[query]
#[param]
#[body]
struct GoodMan<'a> {
    #[serde(default)]
    id: i64,
    #[serde(default)]
    username: &'a str,
    first_name: String,
    last_name: &'a str,
    #[extract(alias("lovers"))]
    lover: &'a str,
}

async fn show(req: Request) -> Response {
    let content = format!(
        r#"<!DOCTYPE html>
    <html>
        <head>
            <title>Parse data</title>
        </head>
        <body>
            <h1>Hello, fill your profile</h1>
            <form action="/{}?username=jobs" method="post">
                <label>First Name:</label><input type="text" name="first_name" />
                <label>Last Name:</label><input type="text" name="last_name" />
                <legend>What is Your Favorite Pet?</legend>
                <input type="checkbox" name="lovers" value="Cats">Cats<br>
                <input type="checkbox" name="lovers" value="Dogs">Dogs<br>
                <input type="checkbox" name="lovers" value="Birds">Birds<br>
                <input type="submit" value="Submit" />
            </form>
        </body>
    </html>
    "#,
        req.param::<&str>("id").unwrap()
    );
    let res = Response::default();
    res.render(Text::Html(content))
}

async fn edit(mut req: Request) -> Result<String> {
    let bad_man = req.extract::<BadMan>().await?;
    let bad_man = format!("Bad Man: {bad_man:#?}");
    let good_man = req.extract::<GoodMan>().await?;
    let good_man = format!("Good Man: {good_man:#?}");
    Ok(format!("{bad_man}\r\n\r\n\r\n{good_man}"))
}

#[tokio::main]
async fn main() -> Result<()> {
    let mut root = Router::default();
    root.get("/:id", show);
    root.post("/:id", edit);
    root.push(Api);
    println!("root = {:#?}", root);
    let listener = hypers::TcpListener::bind("127.0.0.1:7878").await?;
    hypers::listen(root, listener).await
}

#[cfg(test)]
mod test {
    use super::*;
    use feignhttp::{feign, Feign, Result};

    #[derive(Feign)]
    struct Api {
        #[url_path]
        pub email: String,
        #[url_path]
        pub age: u16,
        #[url_path("firstName")]
        pub first_name: String,
    }

    #[feign(url = "http://127.0.0.1:7878/extract")]
    impl Api {
        #[get(path = "/header")]
        async fn header(&self) -> Result<HeaderParams> {}

        #[delete(path = "/{email}/{age}/{firstName}")]
        async fn delete(&self) -> Result<Data> {}

        #[get(path = "/query")]
        async fn query(&self, #[query] data: QueryParams) -> Result<Data> {}

        #[post(path = "/create")]
        async fn create(&self, #[body] data: Data) -> Result<Data> {}

        #[put(path = "/update")]
        async fn update(&self, #[form] data: Data) -> Result<Data> {}

        #[post(path = "/multipart_form")]
        async fn multipart_form(&self, #[form] data: Data) -> Result<Data> {}

        // feignhttp 暂时不支持测试 cookie
        #[get(path = "/set_cookies")]
        async fn set_cookies(&self) -> Result<()> {}

        // feignhttp 暂时不支持测试 cookie
        #[get(path = "/parse_cookies")]
        async fn parse_cookies(&self) -> Result<CookieParams> {}

        #[post(path = "/rename_all")]
        async fn rename_all(&self, #[body] arg: RenameAllArg) -> Result<RenameAllArg> {}
    }

    // #[feign(url = "http://127.0.0.1:7878/extract")]
    // impl Input {
    //     #[post(path = "/extract_input/{id}")]
    //     async fn extract_input(&self) -> Result<Input> {}
    // }

    #[tokio::test]
    async fn test() -> Result<()> {
        let api = Api {
            email: "admin@outlook.com".to_owned(),
            age: 18,
            first_name: "tom".to_owned(),
        };
        let data = Data {
            email: Some("admin@outlook.com".to_owned()),
            age: Some(18),
            first_name: Some("tom".to_owned()),
        };
        let query = QueryParams {
            email: Some("admin@outlook.com".to_owned()),
            age: Some(18),
        };
        // let input = Input {
        //     host: "127.0.0.1:7878".to_owned(),
        //     user_agent: "Feign HTTP".to_owned(),
        //     accept: "application/vnd.github.v3+json".to_owned(),
        //     hypers: "".to_owned(),
        //     rust: "".to_owned(),
        //     id: 11,
        //     name: "admin".to_owned(),
        //     age: 18,
        //     first_name: "li".to_owned(),
        //     last_name: "ming".to_owned(),
        // };
        assert_eq!(
            api.header().await?,
            HeaderParams {
                host: Some("127.0.0.1:7878".to_owned()),
                accept: Some("*/*".to_owned()),
                user_agent: Some("Feign HTTP".to_owned())
            }
        );
        assert_eq!(api.delete().await?, data);
        assert_eq!(api.query(query).await?, data);
        assert_eq!(api.create(data.clone()).await?, data);
        assert_eq!(api.update(data.clone()).await?, data);
        assert_eq!(api.multipart_form(data.clone()).await?, data);
        assert_eq!(api.set_cookies().await?, ());
        let cookies = api.parse_cookies().await?;
        println!("cookie = {:?}", cookies);
        // let input = input.extract_input().await?;
        // println!("input = {:?}", input);
        // assert_eq!()
        Ok(())
    }
}
